"""
合同的大纲抽取
"""
import re
from typing import Dict, Tuple
from core.models import Contract
from core.parse.text_splitter import TextSplitter
from loguru import logger
import cn2an


class TocParser(object):
    # title的若干种匹配种形式
    patterns = {
        'cn_mode_1': re.compile('^([一二三四五六七八九十]+)'),
        'cn_mode_2': re.compile('^第\s*([一二三四五六七八九十]+)\s*[条款章节]*'),
        'cn_mode_3': re.compile('^[\(\（\﹙\（\⁽]\s*([一二三四五六七八九十]+)\s*[\)\）\﹚\）\⁾]'),
        'cn_mode_4': re.compile('^([一二三四五六七八九十]+)\s*[\)\）\﹚\）\⁾]+'),
        'int_mode_1': re.compile('^(\d+)'),
        'int_mode_2': re.compile('^第\s*(\d+)\s*[条款章节]*'),
        'int_mode_3': re.compile('^[\(\（\﹙\（\⁽]\s*(\d+)\s*[\)\）\﹚\）\⁾]'),
        'int_mode_4': re.compile('^(\d+)\s*[\)\）\﹚\）\⁾]'),
        'float_mode_1': re.compile('^(\d+\.\d+)'),
        'float_mode_2': re.compile('^第\s*(\d+\.\d+)\s*[条款章节]*'),
        'float_mode_3': re.compile('^[\(\（\﹙\（\⁽]\s*(\d+\.\d+)\s*[\)\）\﹚\）\⁾]'),
        'float_mode_4': re.compile('^(\d+\.\d+)\s*[\)\）\﹚\）\⁾]')
    }

    # 模式层级排序
    sort_levels = ['cn_mode_1', 'cn_mode_2', 'int_mode_1', 'int_mode_2',
                   'cn_mode_4', 'cn_mode_3', 'int_mode_4', 'int_mode_3',
                   'float_mode_2', 'float_mode_1', 'float_mode_4', 'float_mode_3']

    def __init__(self):
        self.text_spliter = TextSplitter(chunk_size=100, chunk_overlap=0)

    def parse(self, contract: Contract):
        """
        获取大纲目录, 并指出缺失的大纲
        """
        # 先通过正则检索各段落是否是标题段落，以及标题的模式和编号。
        titles = dict()
        for paragraph in contract.paragraphs:
            content = paragraph.get('content', '').strip()
            if content == '':
                continue
            mode, number = TocParser.title_match(content)
            if mode is not None:
                if mode not in titles:
                    titles[mode] = []
                titles[mode].append({
                    'title_mode': mode,
                    'title_number': number,
                    'paragraph': paragraph
                })

        # # 再检查标题是否有缺失遗漏的
        # missed = self.missed_check(titles)
        # # 补上缺失的标题, TODO

        # 标题层级排序
        sorted_titles = sorted(titles.items(), key=lambda x: TocParser.sort_levels.index(x[0]))

        # 只取前两层标题返回
        toc = dict()
        for level, (mode, mode_titles) in enumerate(sorted_titles[0: 2]):
            label = '##' if level == 0 else '###'
            for item in mode_titles:
                content = item['paragraph']['content'].strip()
                chunks = self.text_spliter.split_text(content, separators = \
                    ['\n\n', '\n', ':', '：', '。', '；', ';',' ', '\t'])
                title_text = chunks[0]
                block_index = item['paragraph']['blockIndex']
                toc[block_index] = f'{label} {title_text}'
        return toc

    @staticmethod
    def title_match(content) -> Tuple:
        """ 段落标题检索匹配, 返回(匹配模式，匹配的数字编号) """
        for mode, pattern in TocParser.patterns.items():
            reg = pattern.search(content)
            if reg is not None:
                match = reg.group(1)
                if mode.startswith('cn_mode'):
                    while len(match) > 0:
                        try:
                            number = cn2an.cn2an(match)
                            break
                        except Exception as e:
                            logger.error(e)
                            match = match[:-1]

                elif mode.startswith('int_mode'):
                    number = int(match)
                else:
                    number = float(match)
                return mode, number
        return None, None

    @staticmethod
    def missed_check(titles: Dict) -> Dict:
        """
        检查标题是否有缺失, 返回缺失的标题位置区间
        """
        missed = dict()
        for mode, mode_titles in titles.items():
            mode_missed = []
            for idx in range(len(mode_titles) - 1):
                cur_item = mode_titles[idx]
                cur_number = mode_titles[idx]['title_number']
                next_item = mode_titles[idx + 1]
                next_numer = next_item[0]
                if 1 < next_numer - cur_number < 5:
                    mode_missed.append({
                        'miss_start': cur_item,
                        'miss_end': next_item
                    })
            if len(mode_missed) > 0:
                missed[mode] = mode_missed
        return missed